/*
 * Copyright (c) 2007-2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.sun.org.apache.bcel.internal.classfile;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache BCEL" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache BCEL", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import com.sun.org.apache.bcel.internal.Constants;
import java.io.*;
import java.util.HashMap;

/**
 * Abstract super class for <em>Attribute</em> objects. Currently the
 * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>,
 * <em>Exceptiontable</em>, <em>LineNumberTable</em>,
 * <em>LocalVariableTable</em>, <em>InnerClasses</em> and
 * <em>Synthetic</em> attributes are supported. The
 * <em>Unknown</em> attribute stands for non-standard-attributes.
 *
 * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
 * @see     ConstantValue
 * @see     SourceFile
 * @see     Code
 * @see     Unknown
 * @see     ExceptionTable
 * @see     LineNumberTable
 * @see     LocalVariableTable
 * @see     InnerClasses
 * @see     Synthetic
 * @see     Deprecated
 * @see     Signature
*/
public abstract class Attribute implements Cloneable, Node, Serializable {
    protected int name_index; // Points to attribute name in constant pool
    protected int length; // Content length of attribute field
    protected byte tag; // Tag to distiguish subclasses
    protected ConstantPool constant_pool;

    protected Attribute(byte tag, int name_index, int length, ConstantPool constant_pool) {
        this.tag = tag;
        this.name_index = name_index;
        this.length = length;
        this.constant_pool = constant_pool;
    }

    /**
     * Called by objects that are traversing the nodes of the tree implicitely
     * defined by the contents of a Java class. I.e., the hierarchy of methods,
     * fields, attributes, etc. spawns a tree of objects.
     *
     * @param v Visitor object
     */
    public abstract void accept(Visitor v);

    /**
     * Dump attribute to file stream in binary format.
     *
     * @param file Output file stream
     * @throws IOException
     */
    public void dump(DataOutputStream file) throws IOException {
        file.writeShort(name_index);
        file.writeInt(length);
    }

    private static HashMap readers = new HashMap();

    /** Add an Attribute reader capable of parsing (user-defined) attributes
     * named "name". You should not add readers for the standard attributes
     * such as "LineNumberTable", because those are handled internally.
     *
     * @param name the name of the attribute as stored in the class file
     * @param r the reader object
     */
    public static void addAttributeReader(String name, AttributeReader r) {
        readers.put(name, r);
    }

    /** Remove attribute reader
     *
     * @param name the name of the attribute as stored in the class file
     */
    public static void removeAttributeReader(String name) {
        readers.remove(name);
    }

    /* Class method reads one attribute from the input data stream.
     * This method must not be accessible from the outside.  It is
     * called by the Field and Method constructor methods.
     *
     * @see    Field
     * @see    Method
     * @param  file Input stream
     * @param  constant_pool Array of constants
     * @return Attribute
     * @throws  IOException
     * @throws  ClassFormatException
     */
    public static final Attribute readAttribute(DataInputStream file, ConstantPool constant_pool) throws IOException, ClassFormatException {
        ConstantUtf8 c;
        String name;
        int name_index;
        int length;
        byte tag = Constants.ATTR_UNKNOWN; // Unknown attribute

        // Get class name from constant pool via `name_index' indirection
        name_index = (int) file.readUnsignedShort();
        c = (ConstantUtf8) constant_pool.getConstant(name_index, Constants.CONSTANT_Utf8);
        name = c.getBytes();

        // Length of data in bytes
        length = file.readInt();

        // Compare strings to find known attribute
        for (byte i = 0; i < Constants.KNOWN_ATTRIBUTES; i++) {
            if (name.equals(Constants.ATTRIBUTE_NAMES[i])) {
                tag = i; // found!
                break;
            }
        }

        // Call proper constructor, depending on `tag'
        switch (tag) {
            case Constants.ATTR_UNKNOWN:
                AttributeReader r = (AttributeReader) readers.get(name);

                if (r != null)
                    return r.createAttribute(name_index, length, file, constant_pool);
                else
                    return new Unknown(name_index, length, file, constant_pool);

            case Constants.ATTR_CONSTANT_VALUE:
                return new ConstantValue(name_index, length, file, constant_pool);

            case Constants.ATTR_SOURCE_FILE:
                return new SourceFile(name_index, length, file, constant_pool);

            case Constants.ATTR_CODE:
                return new Code(name_index, length, file, constant_pool);

            case Constants.ATTR_EXCEPTIONS:
                return new ExceptionTable(name_index, length, file, constant_pool);

            case Constants.ATTR_LINE_NUMBER_TABLE:
                return new LineNumberTable(name_index, length, file, constant_pool);

            case Constants.ATTR_LOCAL_VARIABLE_TABLE:
                return new LocalVariableTable(name_index, length, file, constant_pool);

            case Constants.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
                return new LocalVariableTypeTable(name_index, length, file, constant_pool);

            case Constants.ATTR_INNER_CLASSES:
                return new InnerClasses(name_index, length, file, constant_pool);

            case Constants.ATTR_SYNTHETIC:
                return new Synthetic(name_index, length, file, constant_pool);

            case Constants.ATTR_DEPRECATED:
                return new Deprecated(name_index, length, file, constant_pool);

            case Constants.ATTR_PMG:
                return new PMGClass(name_index, length, file, constant_pool);

            case Constants.ATTR_SIGNATURE:
                return new Signature(name_index, length, file, constant_pool);

            case Constants.ATTR_STACK_MAP:
                return new StackMap(name_index, length, file, constant_pool);

            default: // Never reached
                throw new IllegalStateException("Ooops! default case reached.");
        }
    }

    /**
     * @return Length of attribute field in bytes.
     */
    public final int getLength() {
        return length;
    }

    /**
     * @param Attribute length in bytes.
     */
    public final void setLength(int length) {
        this.length = length;
    }

    /**
     * @param name_index of attribute.
     */
    public final void setNameIndex(int name_index) {
        this.name_index = name_index;
    }

    /**
     * @return Name index in constant pool of attribute name.
     */
    public final int getNameIndex() {
        return name_index;
    }

    /**
     * @return Tag of attribute, i.e., its type. Value may not be altered, thus
     * there is no setTag() method.
     */
    public final byte getTag() {
        return tag;
    }

    /**
     * @return Constant pool used by this object.
     * @see ConstantPool
     */
    public final ConstantPool getConstantPool() {
        return constant_pool;
    }

    /**
     * @param constant_pool Constant pool to be used for this object.
     * @see ConstantPool
     */
    public final void setConstantPool(ConstantPool constant_pool) {
        this.constant_pool = constant_pool;
    }

    /**
     * Use copy() if you want to have a deep copy(), i.e., with all references
     * copied correctly.
     *
     * @return shallow copy of this attribute
     */
    public Object clone() {
        Object o = null;

        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace(); // Never occurs
        }

        return o;
    }

    /**
     * @return deep copy of this attribute
     */
    public abstract Attribute copy(ConstantPool constant_pool);

    /**
     * @return attribute name.
     */
    public String toString() {
        return Constants.ATTRIBUTE_NAMES[tag];
    }
}
